﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Util.Data.Iterators
{
    public interface IInputIterator<T>
    {
        T Value { get; }
    }

    public interface IOutputIterator<T>
    {
        T Value { set; }
    }

    public interface IForwardIterator<T>
    {
        void Increment();
    }

    public interface IBackwardIterator<T>
    {
        void Decrement();
    }

    public interface IBidirectionalIterator<T> :
        IForwardIterator<T>, IBackwardIterator<T>
    {
    }

    public interface IRandomIterator<T> : IBidirectionalIterator<T>
    {
        T this[int index] { get; set; }
    }

    // Wraps iterator around an IEnumerator.
    // Note: it would be more efficient if we take an extra template
    // parameter TEnumerator, so that if TEnumerator is a struct, it 
    // can avoid boxing. But for simplicity sake, we do the simple
    // thing to take in an IEnumerator directly.
    public struct IteratorAdaptor<T> : IForwardIterator<T>, IInputIterator<T>
    {
        IEnumerator<T> enumerator;

        public IteratorAdaptor(IEnumerator<T> enumerator)
        {
            this.enumerator = enumerator;
            Increment();
        }

        public void Increment()
        {
            if (enumerator == null)
                throw new InvalidOperationException("Cannot increment iterator past the end.");

            if (!enumerator.MoveNext())
                enumerator = null;
        }

        public T Value
        {
            get
            {
                if (enumerator == null)
                {
                    throw new InvalidOperationException("Cannot get value past the end.");
                }
                return enumerator.Current;
            }
            //set
            //{
            //    throw new NotSupportedException("IEnumerator does not support setting a value.");
            //}
        }

        public static bool operator ==(IteratorAdaptor<T> a, IteratorAdaptor<T> b)
        {
            return a.enumerator == b.enumerator;
        }

        public static bool operator !=(IteratorAdaptor<T> a, IteratorAdaptor<T> b)
        {
            return !(a == b);
        }

        public override bool Equals(object obj)
        {
            return (obj is IteratorAdaptor<T>) && (this == (IteratorAdaptor<T>)obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public static readonly IteratorAdaptor<T> End = new IteratorAdaptor<T>();
    }

    public static class IteratorExtensions
    {
        public static IteratorAdaptor<T> BeginIterator<T>(
            this IEnumerable<T> collection)
        {
            if (collection == null)
                throw new ArgumentNullException("collection");

            return new IteratorAdaptor<T>(collection.GetEnumerator());
        }

        public static IteratorAdaptor<T> EndIterator<T>(
            this IEnumerable<T> collection)
        {
            return IteratorAdaptor<T>.End;
        }
    }

    // A sample iterator.
    //public struct ArrayIterator<T> : IForwardIterator<T>
    //{
    //
    //}

    // The key point is to enumerate a binary tree like a list.

}
